package com.lsh.oms.provider.service.payment;

import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.dubbo.rpc.protocol.rest.support.ContentType;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.lsh.oms.api.model.order.OrderPayNotifyDTO;
import com.lsh.oms.api.model.order.OrderReceiptPayNotifyDTO;
import com.lsh.oms.api.service.payment.IOrderPaymentOnlineNotifyRestService;
import com.lsh.oms.core.constant.ReturnCode;
import com.lsh.oms.core.enums.OrderOperateType;
import com.lsh.oms.core.enums.OrderStatus;
import com.lsh.oms.core.enums.PayChannel;
import com.lsh.oms.core.enums.PayStatus;
import com.lsh.oms.core.exception.BusinessException;
import com.lsh.oms.core.model.email.EmailModel;
import com.lsh.oms.core.model.order.OrderHead;
import com.lsh.oms.core.model.payment.BillPaymentInfo;
import com.lsh.oms.core.proxy.handler.AsyncTaskHandler;
import com.lsh.oms.core.proxy.handler.OrderConcurrentHandler;
import com.lsh.oms.core.proxy.service.EsProxy;
import com.lsh.oms.core.service.asynservice.OmsEventBus;
import com.lsh.oms.core.service.order.OrderUpdateService;
import com.lsh.oms.core.service.payment.BillPaymentService;
import com.lsh.oms.core.service.payment.PaymentService;
import com.lsh.oms.provider.service.BaseService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.math.BigDecimal;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * Project Name: OrderPaymentOnlineNotifyRestService
 * 北京链商电子商务有限公司
 *
 * @author wangliutao
 * Date: 18/5/29
 * Package Name: com.lsh.oms.provider.service.payment
 * Description:订单在线支付,异步回调的接口
 */
@Service(protocol = "rest", timeout = 30000)
@Path("order/pay/online")
@Consumes({MediaType.APPLICATION_JSON})
@Produces({ContentType.APPLICATION_JSON_UTF_8})
public class OrderPayNotifyRestServiceImpl extends BaseService implements IOrderPaymentOnlineNotifyRestService {
    private static final Logger logger = LoggerFactory.getLogger(OrderPayNotifyRestServiceImpl.class);

    @Autowired
    private PaymentService paymentService;

    @Autowired
    private OrderUpdateService orderUpdateService;

    @Autowired
    private BillPaymentService billPaymentService;

    @Autowired
    private EsProxy esProxy;

    @Value("${run.system}")
    private String system;

    @POST
    @Path("notify")
    @Override
    public String payNotify(OrderReceiptPayNotifyDTO payNotify) {
        logger.info("[在线支付--回调] orderReceiptPayNotifyDTO is {}", JSON.toJSONString(payNotify));

        JSONObject res = new JSONObject();
        JSONObject content = new JSONObject();
        res.put("ret", "0");

        if (!"SUCCESS".equals(payNotify.getPay_code())) {
            res.put("ret", "1000");
            return res.toJSONString();
        }

        String payTradeid = payNotify.getTrade_id();
        Long code = 0L;
        try {

            BillPaymentInfo billPaymentInfo = billPaymentService.selectOne(payTradeid);
            if (billPaymentInfo == null) {
                res.put("ret", "1000");
                return res.toJSONString();
            }

            code = billPaymentInfo.getBillCode();
            OrderHead orderHead = this.paymentService.getOrderHead(code);
            if (orderHead == null) {
                throw new BusinessException(ReturnCode.REQUEST_FAIL, "订单信息有误");
            }

            if (!OrderConcurrentHandler.lockOrder(code)) {
                throw new BusinessException(ReturnCode.REQUEST_FAIL, "订单正在处理中……");
            }

            this.validate(orderHead);

            if (orderHead.getOrderStatus().compareTo(OrderStatus.ORDER_NO_EDIT.getIndex()) >= 0 && orderHead.getOrderStatus().compareTo(OrderStatus.ORDER_CANCEL_SYSTEM.getIndex()) != 0) {
                logger.info("[在线支付--回调] 幂等处理 order status is {}", orderHead.getOrderStatus());
                content.put("return_code", "SUCCESS");
                content.put("return_msg", "OK");

                res.put("content", content);
                return res.toJSONString();
            }

            if (this.updateBillPayment(payNotify, billPaymentInfo) <= 0) {
                res.put("ret", "1000");
                return res.toJSONString();
            }

            boolean updateOrderFlag = true;
            BigDecimal orderMoney = orderHead.getExpectPayAmount().add(orderHead.getFreight());
            if (payNotify.getChannel_type().equals(PayChannel.BALANCE_PAYMENT.getCode())) {
                if (new BigDecimal(payNotify.getPayment_amount()).compareTo(orderMoney) < 0) {
                    updateOrderFlag = false;
                }
            }

            if (updateOrderFlag) {
                this.updateOrderStatus(payNotify, orderHead, this.getVenderId());
            } else {
                this.updateOrderStatusBalance(payNotify, orderHead);
            }

            content.put("return_code", "SUCCESS");
            content.put("return_msg", "OK");
        } catch (BusinessException e) {
            logger.error("[在线支付--回调] msg: " + e.getMessage(), e);
            this.sendEmail(e.getMessage(), payNotify);
            res.put("ret", "1000");
            content.put("return_code", "FAIL");
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            this.sendEmail(e.getMessage(), payNotify);
            res.put("ret", "1000");
            content.put("return_code", "FAIL");
        } finally {
            try {
                OrderConcurrentHandler.unlockOrder(code);
            } catch (Exception e) {
                logger.error("[在线支付--回调] redis操作异常", e);
            }
        }

        res.put("content", content);
        return res.toJSONString();
    }

    /**
     * @param payNotify
     * @param billPaymentInfo
     * @return
     */
    private int updateBillPayment(OrderReceiptPayNotifyDTO payNotify, BillPaymentInfo billPaymentInfo) {
        BillPaymentInfo billPaymentInfo4update = new BillPaymentInfo();
        billPaymentInfo4update.setPayTradeId(billPaymentInfo.getPayTradeId());
        billPaymentInfo4update.setPaySequence(payNotify.getPay_payment_no());
        billPaymentInfo4update.setPayStatus(PayStatus.PAID.getValue());
        billPaymentInfo4update.setUpdateTime(new Date());
        billPaymentInfo4update.setPayTime(new Date());
        return billPaymentService.updateByPayTradeId(billPaymentInfo4update);
    }

    /**
     * @param payNotify
     * @param orderHead
     */
    private void updateOrderStatus(OrderReceiptPayNotifyDTO payNotify, OrderHead orderHead, Long venderId) {
        OrderHead orderHead4update = paymentService.initOrderHead4update(payNotify.getChannel_type(), new BigDecimal(payNotify.getPayment_amount()), payNotify.getPay_payment_no(), orderHead);
        logger.info("[在线支付--回调] update db [start], orderHead: {}", JSON.toJSONString(orderHead4update));

        boolean addTaskFlag = true;
        if (OrderStatus.ORDER_NO_EDIT.getIndex() == orderHead4update.getOrderStatus()) {
            addTaskFlag = false;
        }

        this.orderUpdateService.updateOrderHead(orderHead.getOrderCode(), orderHead4update, addTaskFlag, venderId);
        logger.info("[在线支付--回调] update db [end]");
        this.notifyEs(orderHead, orderHead4update);
    }

    /**
     * @param payNotify
     * @param orderHead
     */
    private void updateOrderStatusBalance(OrderReceiptPayNotifyDTO payNotify, OrderHead orderHead) {
        int time = (int) TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
        OrderHead orderHead4update = new OrderHead();
        orderHead4update.setUpdateTime(time);
        orderHead4update.setPayReliefAmout(new BigDecimal(payNotify.getPayment_amount()));
        this.orderUpdateService.updateOrderHead(orderHead.getOrderCode(), orderHead4update);
        logger.info("[在线支付--回调] update db [end]");
        //没有修改订单状态
//        this.notifyEs(orderHead, orderHead4update);
    }

    /**
     * @param orderHead
     * @param orderHead4update
     */
    private void notifyEs(OrderHead orderHead, OrderHead orderHead4update) {
        // TODO: 异步更新ES
        String uuid = UUID.randomUUID().toString();

        OrderHead oh = new OrderHead();
        oh.setOrderCode(orderHead.getOrderCode());
        oh.setPayStatus(orderHead4update.getPayStatus());
        oh.setOrderStatus(orderHead4update.getOrderStatus());
        AsyncTaskHandler.executeAsyncTask(esProxy.newAsyncEditTask(EsProxy.transformToOrderHeadForEs(oh), uuid));

        logger.info("[在线支付--回调] insert operate log! orderCode: {}", orderHead.getOrderCode());
        this.paymentService.addOperateLog(orderHead, OrderOperateType.ORDER_PAY, orderHead4update.getOrderStatus());
        logger.info("[在线支付--回调] Async update ElasticSearch! uuid: {}, orderCode: {}", uuid, orderHead.getOrderCode());
    }

    /**
     * @param orderHead
     */
    private void validate(OrderHead orderHead) {
        if (!orderHead.getOrderStatus().equals(OrderStatus.ORDER_NEW.getIndex())) {
            this.sendEmail("在线支付回调：状态异常，状态是 " + orderHead.getOrderStatus() + ", 订单号：" + orderHead.getOrderCode());
        }
        if (orderHead.getOrderStatus().equals(OrderStatus.ORDER_CANCEL_CUSTOMER.getIndex())
                || orderHead.getOrderStatus().equals(OrderStatus.ORDER_CANCEL_CUSTOMERSERVICE.getIndex())) {
            throw new BusinessException(ReturnCode.REQUEST_FAIL, "订单支付状态不正确");
        }
    }

    private void sendEmail(String msg, OrderReceiptPayNotifyDTO payNotify) {
        StringBuilder sb = new StringBuilder("【警告】在线支付--回调,出现异常!!");
        sb.append("\n").append("回调参数: ").append(JSON.toJSONString(payNotify))
                .append("\n").append("异常信息: ").append(msg);

        this.sendEmail(sb.toString());
    }

    private void sendEmail(String msg, OrderPayNotifyDTO payNotify) {
        StringBuilder sb = new StringBuilder("【警告】在线支付--回调,出现异常!!");
        sb.append("\n").append("回调参数: ").append(JSON.toJSONString(payNotify))
                .append("\n").append("异常信息: ").append(msg);

        this.sendEmail(sb.toString());
    }

    private void sendEmail(String msg) {
        // 邮件报警
        String title = "[" + system + "][oms-OrderPaymentOnlineNotifyRestService]";
        String[] persons = {"wangliutao@lsh123.com"};

        OmsEventBus.post(new EmailModel(msg, title, persons));
    }
}
